<template>
  <div class="map-container">
    <div ref="mapContainerDom" class="map-container"></div>
    <div class="map-ctl">
      <button @click="mapExport()">全图导出</button>
      <button @click="startDraw()">框选区域</button>
      <button @click="frameSelectExport()">框选导出</button>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { OlUtil } from '../../lib/index'
import { onMounted, nextTick, ref, onBeforeUnmount } from 'vue'
import { Map } from 'ol'
import { get, Projection } from 'ol/proj'
import { XYZ, WMTS } from 'ol/source'
import { Draw } from 'ol/interaction'
import VectorSource from 'ol/source/Vector'
import { Polygon } from 'ol/geom'
import VectorLayer from 'ol/layer/Vector'
import { Stroke, Style } from 'ol/style'
import { createBox } from 'ol/interaction/Draw'
const mapConfig = {
  defaultShowMap: 'img',
  imgMapType: 'WMTS',
  imgMapUrl: 'https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer',
  vecMapType: 'XYZ',
  vecMapUrl: 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}',
  projectionCode: 'EPSG:3857',
  center: [81.8018641, 41.7792352],
  zoom: 15,
  maxZoom: 20,
  minZoom: 4,
  imgBaseUrl: ''
}

const olutil = new OlUtil()
let map: Map
const mapLoaded = ref(false)
let observer: ResizeObserver | null = null // 地图dom大小监听
const mapContainerDom = ref() // 地图容器

let mapProjection: Projection | undefined
const projectionCode = mapConfig.projectionCode || 'EPSG:3857'
if (projectionCode === 'EPSG:4490') {
  mapProjection = olutil.defineProjection_4490()
} else {
  mapProjection = get(projectionCode) as Projection
}
let ram: any
if (mapProjection) {
  ram = olutil.getResolutionsAndMids(mapProjection) // 获取分辨率和层级矩阵
}
const showMapType = ref(mapConfig.defaultShowMap) // 当前显示底图类型

// 加载地图
const loadMap = (id: string, mapUrl: string, type: string) => {
  let layer
  if (type === 'XYZ') {
    layer = olutil.loadXYZLayer({
      id,
      url: mapUrl,
      projection: mapConfig.projectionCode,
      crossOrigin: 'anonymous'
    })
  } else if (type === 'WMTS') {
    layer = olutil.loadWMTSLayer({
      id,
      url: mapUrl,
      matrixSet: 'default028mm',
      projection: mapProjection,
      resolutions: ram.resolutions,
      matrixIds: ram.matrixIds,
      crossOrigin: 'anonymous'
    })
  }
  layer && layer.setVisible(mapConfig.defaultShowMap === id)

  // 设置跨域属性
  const source = layer?.getSource() as XYZ | WMTS
  source.setTileLoadFunction((tile: any, url) => {
    const image = tile.getImage() as HTMLImageElement
    image.crossOrigin = 'anonymous' // 为每个瓦片设置跨域属性
    image.src = url
  })
  return layer
}
// 初始化地图
const initMap = () => {
  const vecMapLayer = loadMap('vec', mapConfig.vecMapUrl, mapConfig.vecMapType)
  const imgMapLAyer = loadMap('img', mapConfig.imgMapUrl, mapConfig.imgMapType)

  map = olutil.initMap(mapContainerDom.value, {
    // projection: 'EPSG:3857',
    center: mapConfig.center || [87.62, 43.83],
    zoom: mapConfig.zoom || 8,
    minZoom: mapConfig.minZoom || 6,
    maxZoom: mapConfig.maxZoom || 18,
    layers: [imgMapLAyer!, vecMapLayer!]
  })
}

// 生成当前时间
const getCurrentTime = () => {
  // 生成年月日时分秒格式的时间戳
  const now = new Date()
  const year = now.getFullYear()
  const month = String(now.getMonth() + 1).padStart(2, '0')
  const day = String(now.getDate()).padStart(2, '0')
  const hours = String(now.getHours()).padStart(2, '0')
  const minutes = String(now.getMinutes()).padStart(2, '0')
  const seconds = String(now.getSeconds()).padStart(2, '0')
  const timestamp = `${year}-${month}-${day}_${hours}-${minutes}-${seconds}`
  return timestamp
}

// 全图导出
const mapExport = () => {
  if (!map) return

  // 获取地图的canvas元素
  const canvas = map.getTargetElement().querySelector('canvas') as HTMLCanvasElement

  // 创建临时canvas
  const tempCanvas = document.createElement('canvas')
  const ctx = tempCanvas.getContext('2d') as CanvasRenderingContext2D

  // 设置临时canvas尺寸
  tempCanvas.width = canvas.width
  tempCanvas.height = canvas.height

  // 绘制到临时canvas
  ctx.drawImage(canvas, 0, 0)

  // 导出
  const link = document.createElement('a')

  link.download = `map_${getCurrentTime()}.png`
  link.href = tempCanvas.toDataURL('image/png')
  link.click()
}

let draw: Draw | undefined
let selectedArea: Polygon | undefined
const source = new VectorSource()

// 添加标绘交互
const startDraw = () => {
  if (!map) return
  // 清除现有交互
  if (draw) {
    map.removeInteraction(draw)
  }
  const style = new Style({
    stroke: new Stroke({
      color: 'rgba(0, 255, 0, 1)', // 描边颜色
      width: 2 // 描边宽度
    })
  })
  let drawLayer = olutil.getLayer('drawLayer') as VectorLayer
  if (!drawLayer) {
    drawLayer = new VectorLayer({
      source,
      style
    })
    drawLayer.set('id', 'drawLayer')
    map.addLayer(drawLayer)
  } else {
    drawLayer.getSource()?.clear()
  }

  draw = new Draw({
    source: source,
    type: 'Circle', // 通过createBox绘制矩形
    geometryFunction: createBox(),
    style
  })

  map.addInteraction(draw)

  draw.on('drawend', (e) => {
    selectedArea = e.feature.getGeometry() as Polygon
    map.removeInteraction(draw!)
  })
}
// 框选导出
const frameSelectExport = () => {
  if (!map || !selectedArea) return
  let drawLayer = olutil.getLayer('drawLayer') as VectorLayer
  if (!drawLayer) {
    return
  }
  drawLayer.setVisible(false)
  // 等待图层隐藏，防止导出时包含标绘区域
  setTimeout(() => {
    // 获取地图的canvas元素
    const canvas = map.getTargetElement().querySelector('canvas') as HTMLCanvasElement

    // 创建临时canvas
    const tempCanvas = document.createElement('canvas')
    const ctx = tempCanvas.getContext('2d') as CanvasRenderingContext2D

    // 获取标绘区域在canvas中的坐标
    const extent = selectedArea!.getExtent()
    // 获取左下角(minX,minY)和右上角(maxX,maxY)的像素坐标
    const bottomLeft = map.getPixelFromCoordinate([extent[0], extent[1]]) // 左下角
    const topRight = map.getPixelFromCoordinate([extent[2], extent[3]]) // 右上角

    const width = topRight[0] - bottomLeft[0]
    const height = bottomLeft[1] - topRight[1] // 注意Y轴方向

    // 设置临时canvas尺寸
    tempCanvas.width = width
    tempCanvas.height = height

    // 只绘制标绘区域
    ctx.drawImage(
      canvas,
      bottomLeft[0], // 源x (左下角x)
      topRight[1], // 源y (右上角y)
      width, // 源宽度
      height, // 源高度
      0, // 目标x
      0, // 目标y
      width, // 目标宽度
      height // 目标高度
    )

    // 导出
    const link = document.createElement('a')
    link.download = `map-selection_${getCurrentTime()}.png`
    link.href = tempCanvas.toDataURL('image/png')
    link.click()
    drawLayer.setVisible(true)
  }, 200)
}

onMounted(() => {
  initMap()
  nextTick(() => {
    map &&
      map.once('rendercomplete', () => {
        mapLoaded.value = true
        // 监听地图dom大小变化
        observer = new ResizeObserver(() => {
          map && map.updateSize()
        })
        observer.observe(mapContainerDom.value)
      })
  })
})
onBeforeUnmount(() => {
  try {
    observer && observer.unobserve(mapContainerDom.value) // 移除监听
  } catch (err) {
    console.log(err)
  }
})
</script>
<script lang="ts">
export default {
  name: 'ScreenShotMap'
}
</script>
<style lang="scss" scoped>
.map-container {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: hidden;
  .base-map-ctl {
    position: absolute;
    right: 0.2rem;
    bottom: 0.1rem;
    display: flex;
    background: rgba(31, 30, 30, 0.5);
    box-shadow: 2px 2px rgba(0, 0, 0, 0.4);
    border-radius: 3px;
    padding: 0.1rem;
    .map-type-img {
      height: 60px;
      width: 86px;
      cursor: pointer;
      border: 2px solid transparent;
    }

    .map-type-img-selected {
      border: 2px solid #2d8cf0;
    }
    .map-type-img:last-child {
      margin-left: 0.05rem;
    }
  }
  .map-ctl {
    position: absolute;
    left: 0.2rem;
    top: 0.2rem;
    display: flex;
    flex-direction: column;
    background: rgba(0, 0, 0, 0.5);
    padding: 0.1rem 0.08rem;
    border-radius: 4px;
    .map-ctl-item {
      margin-bottom: 0.1rem;
    }
    .map-ctl-item:last-child {
      margin-bottom: 0;
    }
  }
  .get-position-title {
    position: absolute;
    border: 1px solid #2d8cf0;
    background: white;
    padding: 0 4px;
    border-radius: 5px;
    pointer-events: none;
  }
}
</style>
